<?php
/**
 * auth
 *
 * @version:	0.1a
 * @package:	global
 * @author:	alex_ez
 * @created:	27.01.2009 01:52:13
 *
 * @copyright:	(c) 2009 alex <qfox@ya.ru> yaroshevich aka alex_ez. All rights reserved.
 */

define( 'AJAX_AUTH_COMPLETED',		200 );
define( 'AJAX_AUTH_ERROR',		400 );
define( 'AJAX_AUTH_INVALIDPASSWORD',	401 );
define( 'AJAX_AUTH_INVALIDLOGIN',	402 );
define( 'AJAX_AUTH_AUTHORIZED',		403 );
define( 'AJAX_AUTH_INTERNALERROR',	501 );
define( 'AJAX_AUTH_DATABASEFAILED',	502 );


class authController extends controller
{
	protected $users = null;
	protected $path = null;
	
	public function onstart( )
	{
		$this ->visitor = visitor::__getMe( );
		$this ->users = new users( );
		$this ->path = '/'.trim(_::pick(config::__getMe( ) ->auth__controller, 'auth'),"\r\n\t /\\").'/';
		$this ->frame = _::pick( config::__getMe( ) ->auth__frame, '!auth.tpl' );
		// nothing to do
	}
	
	public function resign( ) { return $this ->signinRefresh( ); }
	public function signinRefresh( )
	{
		$visitor = visitor::__getMe( );
		if( !$visitor ->isSigned() ) return;
		
		$users = is_null( $this ->users ) ? new users( ) : $this ->users;
		$visitor ->signin( $users ->get( $visitor ->getUserId( ) ) );
	}
	
	
	public function showIndexPage( $p, $o )
	{
		$path = str_replace( '//', '/', str_replace( '/person/', '/', '/'.$this ->visitor ->user['state'].'/' ) );
 		response::__getMe() ->seeother( $path );
	}
	
	public function showSigninPage( $p, $o )
	{
		$r = request::__getMe( );
		$visitor = visitor::__getMe( );
		
		if( $visitor ->isSigned() )
		{
			/*$referer = isset( $visitor ->realReferer ) ? $visitor ->realReferer : $visitor ->referer;
			$referer = $referer == '/' ? $this ->path : ( $referer ? $referer : '/' );*/
			$referer = '/';
			$this ->signinRefresh( );
			response::__getMe() ->seeother( $referer );
		}
		
/*		echo "<pre>";
		print_r( array( 'ref' => $r ->getReferer( ), 'ses' => $visitor ->referer ) );
		echo "</pre>";
		die;*/
		$getreferer = $r ->getReferer( );
		$visitor ->referer = ( $getreferer != '/' && strpos( $getreferer, $this ->path ) !== 0
			&& strpos( $getreferer, '/files/' ) !== 0 && strpos( $getreferer, '/ajax/' ) !== 0 )
				? $r ->getReferer( )
				: ( $visitor ->referer ? $visitor ->referer : '/' );
		
		if( !$r ->isPostMethod( ) )
		{
			return array( 'user' => $visitor ->login, 'referer' => $visitor ->referer, 'auth__controller' => $this ->path );
		}
		
		$p = $r ->p( array( 'login', 'password', 'store' ) );
		
		if( strlen( $p['login'] ) == 0
		 || strlen( $p['password'] ) == 0 )
		{
			$visitor ->auth_error = 'empty';
			$visitor ->needAuth( );
		};

		if( strlen( $p['login'] ) < 3
		 || strlen( $p['password'] ) < 3 )
		{
			$visitor ->auth_error = 'tooshort';
			$visitor ->needAuth( );
		};
		
		$id = (int)( $this->users->testLoginPass( $p['login'], $p['password'] ) );
		if( !$id )
		{
			$visitor ->auth_login = $visitor ->login = $p['login'];
			$visitor ->auth_error = 'incorrect';
			$visitor ->needAuth( );
		}
		
		$store = is_null($p['store'])
			? null
			: ( $p['store'] ? (strtotime( "next month" ) - time()) : 0 );
		
		$visitor ->signin( $this ->users ->get( $id ), $store );
		try{ $this ->users ->edit( $id, array( 'sid'  => $visitor ->sid(1) ) ); } catch( Exception $e ) { }
		
		$referer = isset( $visitor ->realReferer ) ? $visitor ->realReferer : $visitor ->referer;
		fb::group( 'auth::signin' );
		FB::log( 'visitor ->referer: '. $visitor ->referer );
		FB::log( 'visitor ->realReferer: '. $visitor ->realReferer );
		FB::log( 'referer: '. $referer );
		unset( $visitor ->referer );
		unset( $visitor ->realReferer );
		fb::groupEnd( );
		
		//
		if( strpos( $referer, '/files' ) === 0
			|| strpos( $referer, '/ajax' ) === 0
			|| strpos( $referer, 'hash' ) !== false )
			$referer = '/';
		
		response::__getMe() ->seeother( $referer == '/' ? $this ->path : $referer );
	}
	
	public function showSignoutPage( $p, $o )
	{
		$r = request::__getMe( );
		$visitor = visitor::__getMe( );
		
		$referer = $r ->getReferer( );
		$referer = ( $referer != '/' && strpos( $referer, $this ->path ) !== 0 ) ? $referer : '/';
		
		$visitor ->signout( );
		response::__getMe() ->seeother( $referer );
	}
	
	public function showProfilePage( $p, $o )
	{
		$visitor = visitor::__getMe();
		$visitor ->needAuth( );
		$p = $this ->users ->get( $visitor ->getUserId(), 0 );
		$p['code'] = $_SESSION['code'] = md5( microtime( 1 ) );
		$birthday = explode('-', $p['birthday']);
		if(isset($birthday[0])) $p['year'] = $birthday[0];
		if(isset($birthday[1])) $p['month'] = $birthday[1];
		if(isset($birthday[2])) $p['day'] = $birthday[2];
		fb::log($this ->users ->get( $visitor ->getUserId() ));
		$p['auth__controller'] = $this ->path;
		return $p;
	}
	
	public function showSignupPage( $p, $o )
	{
		$r = request::__getMe( );
		$visitor = visitor::__getMe( );
		if( $visitor ->signed || !$r ->isGetMethod( ) )
			response::__getMe() ->seeother( $visitor ->signed ? $this ->path : $this ->path.'signup/' );
		
		$p = array( 'name' => '', 'surname' => '', 'email' => '', 'login' => '', 'password' => '', 'password2' => '' );
		//$p = array_merge( $p, (array)($r ->p_i) );
		$p['code'] = $_SESSION['code'] = md5( microtime( 1 ) );
		fb::log( 'new hash: '.$p['code'] );
		
		$p['auth__controller'] = $this ->path;
		return $p;
	}
	
	public function showRecoveryPage( $p, $o )
	{
		$p['auth__controller'] = $this ->path;
		return $p;
	}
	
	public function microBlock( $p, $o )
	{
		$v = visitor::__getMe( );
		return array( 'user' => $v ->user, 'auth__controller' => $this ->path );
		//$this ->template = isset( $o['template'] ) ? $o['template'] : 'micro.tpl';
	}
	
	
	// web 2.0
	public function ajaxComplexBlock( )
	{
		$v = visitor::__getMe( );
		return array( );
	}
	
	public function ajaxSignup( )
	{
		// init
		$r = request::__getMe( );
		if( !$r ->isPostMethod( ) ) response::__getMe( ) ->teapot( );
		
		$v = visitor::__getMe( );
		if( $v ->signed ) return $this ->ajax_r( 'authorized', AJAX_AUTH_AUTHORIZED );
		
		$p = array_merge( array( 'name' => '', 'surname' => '', 'email' => '', 'login' => '', 'password' => '', 'password2' => '' ), (array)($r ->p_data) );
		
		// verifications
		if( $hr = $this ->ajax_hash( $p['code'] ) ) return $hr;
		
		if( !strlen($p['password']) || !strlen($p['password2']) || ($p['password']!=$p['password2']) )
			return $this ->ajax_r( 'passwords not equals', AJAX_AUTH_INVALIDPASSWORD );
		
		if( mb_strlen($p['name']) < 2 || mb_strlen($p['surname']) < 2 )
			return $this ->ajax_r( 'name error' );
		
		fb::log( 'email. len: '.strlen($p['email']).', texo: '.texo::isEMail($p['email']).', free: '.$this ->users ->testEmail($p['email']) );
		if( !strlen($p['email']) || !texo::isEMail($p['email']) || !$this ->users ->testEmail($p['email']) )
			return $this ->ajax_r( 'email error' );
		
		if( !strlen($p['login']) || ($p['login']!=texo::latinate($p['login'])) )
			return $this ->ajax_r( 'login error or empty', AJAX_AUTH_INVALIDLOGIN );
		
		fb::log( 'login. value: '.$p['login'].', free: '.(!$this ->users ->testLogin( $p['login'] )) );
		if( !$this ->users ->testLogin( $p['login'] ) )
			return $this ->ajax_r( 'login not available', AJAX_AUTH_INVALIDLOGIN );
		
		unset( $_SESSION['code'] );
		
		// okay? lets sign up now!
		$uid = $this ->users ->add( array(
			'login' => $p['login'],
			'password' => $p['password']
			) );
		if( $uid<=0 )
			return $this ->ajax_r( 'login not available or database fails', AJAX_AUTH_DATABASEFAILED );
		
		fb::log( 'new user id: '.$uid );
		
		// and properties...
		$this ->users ->edit( $uid, array( 'name' => $p['name'], 'surname' => $p['surname'], 'email' => $p['email'] ) );
		
		$id = (int)( $this->users->testLoginPass( $p['login'], $p['password'] ) );
		if( !$id || $id!=$uid )
			return $this ->ajax_r( 'user failed', AJAX_AUTH_DATABASEFAILED );
		
		$v ->signin( $this ->users ->get( $id ) );
		
		return $this ->ajax_r( 'registered', AJAX_AUTH_COMPLETED );
	}
	
	public function ajaxChecklogin( $p )
	{
		$r = request::__getMe( );
		$g = $r ->p( array( 'login', 'name', 'surname', 'email' ) );
		
		if( !mb_strlen( $g['login'] ) )
			return array( 'error' => 'I\'m a teapot. Coffee is out' );
		
		$g['namel'] = texo::latinate( $g['name'] );
		$g['surnamel'] = texo::latinate( $g['surname'] );
		$guesses = array(
			$g['login'],
			$g['namel'],
			$g['surnamel'],
			$g['namel'].$g['surnamel'],
			);
		
		$ls = $this ->users ->testLogins( $guesses );
		
		$r = array( );
		foreach( $ls as $k => $v )
			if( $v === false ) $r[] = $k;
		
		return array( 'logins' => $r /*, 'guesses' => $guesses, 'list' => $ls*/ );
	}
	
	public function ajaxCheckemail( $p )
	{
		$r = request::__getMe( );
		$e = $r ->p_email;
		
		if( !mb_strlen( $e ) )
			return array( 'error' => 'I\'m a teapot. Coffee is out' );
		
		$free = $this ->users ->testEmail( $e );
		
		return array( 'free' => $free );
	}
	
	public function ajaxEditprofile( )
	{
		// init
		$r = request::__getMe( ); if( !$r ->isPostMethod( ) ) response::__getMe( ) ->teapot( );
		$v = visitor::__getMe( ); if( !$v ->signed ) return $this ->ajax_r( 'not authorized', AJAX_AUTH_AUTHORIZED );
		
		// {"name":"alex","surname":"yaroshevich","login":"qfox","email":"qfox@ya.ru","password":"qwe","password2":"qwe"}
		
		$p = array_merge( array( 'name' => '', 'surname' => '' ), (array)($r ->p_data) );
		$p['birthday'] = '';
		$p['birthday'] = $p['year'].'-'.$p['month'].'-'.$p['day'];
		unset($p['year']);
		unset($p['day']);
		unset($p['month']);
		// verifications
		if( $hr = $this ->ajax_hash( $p['code'] ) ) return $hr;
		
		if( mb_strlen($p['name']) < 2 || mb_strlen($p['surname']) < 2 )
			return $this ->ajax_r( 'name error' );
			
		if(mb_strlen($p['city']) < 2)
			return $this ->ajax_r( 'city error' );
		
		$p = array_intersect_key( $p, array( 'name' => '', 'surname' => '', 'birthday' => '', 'city' => '' ) );
		$_r = $this ->users ->edit( $v ->getUserId(), $p );
		
		if( !$_r )
		{
			return $this ->ajax_r( 'not changed', AJAX_AUTH_DATABASEFAILED );
		}
		return $this ->ajax_r( 'changed', AJAX_AUTH_COMPLETED );
	}
	
	public function ajaxEditAvatar( )
	{
		// init
		$r = request::__getMe( ); if( !$r ->isPostMethod( ) ) response::__getMe( ) ->teapot( );
		$v = visitor::__getMe( ); if( !$v ->signed ) return $this ->ajax_r( 'not authorized', AJAX_AUTH_AUTHORIZED );
		
		$_file = $r ->f_fileField;
		$_del = $r ->p_delField;
		$user_id = $v ->getUserId();
		if($_del == "yes")
		{
			$userAvatarDirectory = DIR_AVATARS.$user_id;
			$_nfilet = realpath( $userAvatarDirectory ).'/';
			unlink( $_nfilet.'0.jpg');
			unlink( $_nfilet.'64.jpg');
			unlink( $_nfilet.'32.jpg');
			
		}
		if( !$_file )
			response::__getMe() ->teapot();
		
		$file = $_file['tmp_name'];
		$gl = gl::__getMe();
		
		if( !$gl ->isImageFile( $file ) )
			return array( 'result' => 'failed', 'error' => "Неподдерживаемый формат файла." );
		
		// touch user avatar directory
		$userAvatarDirectory = DIR_AVATARS.$user_id;
		if( !is_dir( $userAvatarDirectory ) ) @mkdir( $userAvatarDirectory, 0750, 1 );
		
		$_nfilet = realpath( $userAvatarDirectory ).'/';
		$_nfile = $_nfilet.'new.jpg';
		//var_dump( $userAvatarDirectory, DIR_AVATARS, root );
		//return $_nfile;
		$uploaded = $gl ->moveUploadedImage( $file, $_nfile );
		
		if( !$uploaded )
		{
			return array( 'result' => 'failed', 'error' => "Не залилося. Проблема с картинкой." );
		}

		$_n0file = $_nfilet.'0.jpg';
		$_n1file = $_nfilet.'64.jpg';
		$_n2file = $_nfilet.'32.jpg';
		$gl ->saveSquare( $_nfile, $_n0file, 100 );
		$gl ->saveSquare( $_nfile, $_n1file, 64 );
		$gl ->saveSquare( $_nfile, $_n2file, 32 );
		unlink( $_nfile );
		
		echo json_encode( array( 'result' => 'success', 'message' => sprintf( "Загружено!", $_file['name'] ) ) );
		response::__getMe( ) ->done();
	}

	public function ajaxEditPassword( )
	{
		// init
		$r = request::__getMe( ); if( !$r ->isPostMethod( ) ) response::__getMe( ) ->teapot( );
		$v = visitor::__getMe( ); if( !$v ->signed ) return $this ->ajax_r( 'not authorized', AJAX_AUTH_AUTHORIZED );
		
		// {"name":"alex","surname":"yaroshevich","login":"qfox","email":"qfox@ya.ru","password":"vertex","password2":"vertex"}
		$p = array_merge( array( 'password0' => '', 'password' => '', 'password2' => '' ), (array)($r ->p_data) );
		
		// verifications
		if( $hr = $this ->ajax_hash( $p['code'] ) ) return $hr;
		
		if( !strlen($p['password']) || !strlen($p['password2']) || ($p['password']!=$p['password2']) )
			return $this ->ajax_r( 'passwords not equals', AJAX_AUTH_INVALIDPASSWORD );
		
		$u = $this ->users ->get( $v ->getUserId() );
		
		if( !$this ->users ->testLoginPass( $u['login'], $p['password0'] ) )
			return $this ->ajax_r( 'old password mismatch', AJAX_AUTH_INVALIDPASSWORD );
		
		$_r = $this ->users ->editPassword( $u['id'], $p['password'] );
		if( !$_r ) return $this ->ajax_r( 'not changed', AJAX_AUTH_DATABASEFAILED );
		
		return $this ->ajax_r( 'changed', AJAX_AUTH_COMPLETED );
	}
	
	
	// errors generator
	public function ajax_r( $e, $c = AJAX_AUTH_ERROR )
	{
		$r = array( 'error' => $e, 'code' => $c );
		if( $c == AJAX_AUTH_COMPLETED || $c == AJAX_AUTH_AUTHORIZED ) { $r['location'] = $this ->path; } else { $r['hash'] = $_SESSION['code'] = md5( microtime( 1 ) ); }
		fb::log( '@'.$c.': '.$e );
		if( isset( $r['hash'] ) ) fb::log( 'new hash: '.$r['hash'] );
		return $r;
	}
	
	// hash tester
	public function ajax_hash( $code )
	{
		fb::log( 's.hash: '.$_SESSION['code'] );
		fb::log( 'g.hash: '.$code );
		if( !isset( $_SESSION['code'] ) || $_SESSION['code'] != $code )
			return $this ->ajax_r( 'internal error', AJAX_AUTH_INTERNALERROR );
		fb::log( 'hash equals! continue...' );
		return false;
	}

}

?>